using System;
using System.Text;
public class ObjectId : IComparable<ObjectId>, IEquatable<ObjectId>
{
    public static ObjectId ObjectNull = new ObjectId(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 });
    private readonly int _a = 0;
    private readonly int _b = 0;
    private readonly int _c = 0;

    public ObjectId(byte[] bytes)
    {
        if (null == bytes)
            _a = _b = _c = 0;
        else
            FromByteArray(bytes, 0, out _a, out _b, out _c);
    }

    public int CompareTo(ObjectId other)
    {
        int result = ((uint)_a).CompareTo((uint)other._a);
        if (result != 0) { return result; }
        result = ((uint)_b).CompareTo((uint)other._b);
        if (result != 0) { return result; }
        return ((uint)_c).CompareTo((uint)other._c);
    }

    public bool Equals(ObjectId other)
    {
        return
            _a == other._a &&
            _b == other._b &&
            _c == other._c;
    }
    public override bool Equals(object obj)
    {
        if (obj is ObjectId)
        {
            return Equals((ObjectId)obj);
        }
        else
        {
            return false;
        }
    }
    public static bool operator <(ObjectId lhs, ObjectId rhs)
    {
        return lhs.CompareTo(rhs) < 0;
    }

    public static bool operator <=(ObjectId lhs, ObjectId rhs)
    {
        return lhs.CompareTo(rhs) <= 0;
    }

    public static bool operator ==(ObjectId lhs, ObjectId rhs)
    {
        if (!(lhs is ObjectId) && !(rhs is ObjectId))
            return true;

        if ((!(lhs is ObjectId) && (rhs is ObjectId)) || (lhs is ObjectId && !(rhs is ObjectId)))
            return false;

        return lhs.Equals(rhs);
    }

    public static bool operator !=(ObjectId lhs, ObjectId rhs)
    {
        return !(lhs == rhs);
    }

    public static bool operator >=(ObjectId lhs, ObjectId rhs)
    {
        return lhs.CompareTo(rhs) >= 0;
    }

    public static bool operator >(ObjectId lhs, ObjectId rhs)
    {
        return lhs.CompareTo(rhs) > 0;
    }

    public override int GetHashCode()
    {
        int hash = 17;
        hash = 37 * hash + _a.GetHashCode();
        hash = 37 * hash + _b.GetHashCode();
        hash = 37 * hash + _c.GetHashCode();
        return hash;
    }

    private static void FromByteArray(byte[] bytes, int offset, out int a, out int b, out int c)
    {
        a = (bytes[offset + 0] << 24) | (bytes[offset + 1] << 16) | (bytes[offset + 2] << 8)  | bytes[offset + 3];
        b = (bytes[offset + 4] << 24) | (bytes[offset + 5] << 16) | (bytes[offset + 6] << 8)  | bytes[offset + 7];
        c = (bytes[offset + 8] << 24) | (bytes[offset + 9] << 16) | (bytes[offset + 10] << 8) | bytes[offset + 11];
    }
    public byte[] ToByteArray()
    {
        var bytes = new byte[12];
        ToByteArray(bytes, 0);
        return bytes;
    }
    public static string ToHexString(byte[] bytes)
    {
        if (bytes == null)
        {
            throw new ArgumentNullException("bytes");
        }
        var sb = new StringBuilder(bytes.Length * 2);
        foreach (var b in bytes)
        {
            sb.AppendFormat("{0:x2}", b);
        }
        return sb.ToString();
    }
    public override string ToString()
    {
        return ToHexString(ToByteArray());
    }
    public void ToByteArray(byte[] destination, int offset)
    {
        if (destination == null)
        {
            throw new ArgumentNullException("destination");
        }
        if (offset + 12 > destination.Length)
        {
            throw new ArgumentException("Not enough room in destination buffer.", "offset");
        }

        destination[offset + 0] = (byte)(_a >> 24);
        destination[offset + 1] = (byte)(_a >> 16);
        destination[offset + 2] = (byte)(_a >> 8);
        destination[offset + 3] = (byte)(_a);
        destination[offset + 4] = (byte)(_b >> 24);
        destination[offset + 5] = (byte)(_b >> 16);
        destination[offset + 6] = (byte)(_b >> 8);
        destination[offset + 7] = (byte)(_b);
        destination[offset + 8] = (byte)(_c >> 24);
        destination[offset + 9] = (byte)(_c >> 16);
        destination[offset + 10] = (byte)(_c >> 8);
        destination[offset + 11] = (byte)(_c);
    }
}